import math
from io import BytesIO

from fastapi import APIRouter
from matplotlib import pyplot as plt
from minio.error import S3Error
from seaman.core import JsonUtil
import json

from common.method import *
from common.model import InterpolationData
from common.params import ReqSoftParams, SoftParams
from service.DataPreService import read_interpolation_data
from model.soft.cable_tension import run as cable_run
from model.soft.descent_depth import run as descent_run
from strain.cable_strain import run as strain_run
from typing import Dict

router = APIRouter()

# 4.4 仪器和电缆分析
@router.post('/v3/combination/conclusion')
async def instrument_cable_conclusion(params: ReqSoftParams):
    # 计算 MD5 哈希值
    md5_hash = params.calc_md5()
    # 参数预处理
    soft_params: SoftParams = SoftParams(params)
    # 获取井号
    wellId: str = params.wellId
    # 获取间距
    space: float = params.space
    # 获取是否有造斜点
    is_slanting_point = params.is_slanting_point
    # 构建对象名称
    data_object_name_cable_strain = wellId + '/' + md5_hash + '-strain.xlsx'
    # 构建张力对象名称
    data_object_name_cable_tension = wellId + '/' + md5_hash + '-cable_tension.xlsx'
    # 构建可下入深度对象名称
    data_object_name_descent_depth = wellId + '/' + md5_hash + '-descent_depth.xlsx'
    result_cable_strain = []
    result_cable_tension = []
    # 是否加入液体阻力 @TODO 如果为true就加入 否则加0
    is_Not_add_resistance = params.add_liquid_resistance
    try:
        client.stat_object('well', data_object_name_cable_strain)
        print('已存在对应变形量分析数据')
        file_data = client.get_object("well", data_object_name_cable_strain)
        df = pd.read_excel(BytesIO(file_data.read()))
        result_cable_strain = []
        for index, row in df.iterrows():
            result_cable_strain.append(row)
    except S3Error:
        print('不存在变形量分析数据')
        # 获取插值数据
        _list_: List[InterpolationData] = read_interpolation_data(wellId, space, is_slanting_point)
        max_depth: float = _list_[-1].depth
        inc_dict: Dict = {}
        for item in _list_:
            inc_dict[item.depth] = item.inclination
        # soft_params.build_f_hel(inc_dict, max_depth)
        # 计算的间距
        calculate_space = params.calculate_space
        last_depth = _list_[-1].depth
        # 设置间隔深度列表
        desired_depths = [last_depth]
        # 从最后一个深度开始，减去间距，直到小于间距为止 这样就只计算部分深度
        while desired_depths[-1] > calculate_space:
            new_value = desired_depths[-1] - calculate_space
            desired_depths.append(new_value)
            # 对列表进行排序并去除重复值
        desired_depths = sorted(set(desired_depths), reverse=True)
        # 井口摩擦力设置
        wellhead_friction: float = params.wellhead_friction
        # 是否加入液体阻力 @TODO 如果为true就加入 否则加0
        is_Not_add_resistance = params.add_liquid_resistance
        result_cable_strain = strain_run(soft_params, _list_, is_Not_add_resistance, desired_depths, wellhead_friction)
        save('well', data_object_name_cable_strain, result_cable_strain)
    try:
        client.stat_object('well', data_object_name_cable_tension)
        print('已存在对应缆头张力分析数据')
        file_data = client.get_object("well", data_object_name_cable_tension)
        df = pd.read_excel(BytesIO(file_data.read()))
        result_cable_tension = []
        for index, row in df.iterrows():
            result_cable_tension.append(row)
    except S3Error:
        print('不存在缆头张力分析分析数据')
        # 获取插值数据
        _list_: List[InterpolationData] = read_interpolation_data(wellId, space, is_slanting_point)
        max_depth: float = _list_[-1].depth
        inc_dict: Dict = {}
        for item in _list_:
            inc_dict[item.depth] = item.inclination
        # soft_params.build_f_hel(inc_dict, max_depth)
        # 计算的间距
        calculate_space = params.calculate_space
        last_depth = _list_[-1].depth
        # 设置间隔深度列表
        desired_depths = [last_depth]
        # 从最后一个深度开始，减去间距，直到小于间距为止 这样就只计算部分深度
        while desired_depths[-1] > calculate_space:
            new_value = desired_depths[-1] - calculate_space
            desired_depths.append(new_value)
            # 对列表进行排序并去除重复值
        desired_depths = sorted(set(desired_depths), reverse=True)

        # 是否加入液体阻力 @TODO 如果为true就加入 否则加0
        is_Not_add_resistance = params.add_liquid_resistance
        result_cable_tension = cable_run(soft_params, _list_, is_Not_add_resistance, desired_depths)
        save('well', data_object_name_cable_tension, result_cable_tension)
    try:
        client.stat_object('well', data_object_name_descent_depth)
        print('已存在对应可下入深度分析数据')
        file_data = client.get_object("well", data_object_name_descent_depth)
        df = pd.read_excel(BytesIO(file_data.read()))
        result_descent_depth = []
        for index, row in df.iterrows():
            result_descent_depth.append(row)
    except S3Error:
        print('不存在可下入深度分析分析数据')
        # 获取插值数据
        _list_: List[InterpolationData] = read_interpolation_data(wellId, space, is_slanting_point)
        inc_depth: float = _list_[-1].depth
        # 获取给定的井斜角的值
        inc_value = params.inclination
        inc_dict: Dict = {}
        for item in _list_:
            inc_dict[item.depth] = item.inclination
            if item.inclination > inc_value:
                inc_depth = item.depth
                break
        print("当前的井斜角对应的深度为为", inc_depth)
        # 计算的间距
        calculate_space = params.calculate_space
        last_depth = _list_[-1].depth
        # 设置间隔深度列表
        desired_depths = [last_depth]
        # 从最后一个深度开始，减去间距，直到小于间距为止 这样就只计算部分深度
        while desired_depths[-1] > calculate_space:
            new_value = desired_depths[-1] - calculate_space
            desired_depths.append(new_value)
            # 对列表进行排序并去除重复值
        desired_depths = sorted(set(desired_depths), reverse=True)

        # 是否加入液体阻力 @TODO 如果为true就加入 否则加0
        is_Not_add_resistance = params.add_liquid_resistance
        # print(inc_dict)
        result_descent_depth = descent_run(soft_params, inc_depth, _list_, is_Not_add_resistance, desired_depths)
        save('well', data_object_name_descent_depth, result_descent_depth)

    _list_: List[InterpolationData] = read_interpolation_data(wellId, space, is_slanting_point)
    # ******************************************
    # # 拿到拦截的极限下入深度
    hel_depth: float = result_descent_depth[0].depth
    # 是否可以下入到极限深度：井底 能下入就是 True 不能正常就是False
    is_not_descent_bottom: bool = True
    for descent_item in result_descent_depth:
        if descent_item.push_cable_pressure == 1:
            is_not_descent_bottom = False
            hel_depth = descent_item.depth
    # 发生屈曲的迭代开始深度 start_depth 1890 屈曲点 hel_depth 1843
    start_depth: float = 0.0
    # 获取 1883 全部值
    target_item = result_cable_strain[0].F_weak_rate_low
    for f_item in result_cable_strain:
        if f_item.depth == hel_depth:
            target_item = f_item
            break
    # ********************************************
    # 先默认能正常到达井底------------------------2100
    # 最大上提产生位置 F_pull
    max_f_pull: float = 0.0
    max_f_pull_depth: float = 0.0
    # 最小下放产生位置 F_pull
    min_f_push: float = result_cable_strain[0].F_push
    min_f_push_depth: float = result_cable_strain[0].depth
    if is_not_descent_bottom == False:
        min_f_push: float = target_item.F_push
        min_f_push_depth: float = target_item.depth
    for item_tension in result_cable_strain:
        if item_tension.depth <= hel_depth:
            # 计算最大上提F
            if item_tension.F_pull > max_f_pull:
                max_f_pull = item_tension.F_pull
                max_f_pull_depth = item_tension.depth
            # 计算最小下放
            if item_tension.F_push < min_f_push:
                min_f_push = item_tension.F_push
                min_f_push_depth = item_tension.depth
    # 发生屈曲不能正常到达井底------------------------1843

    # 获取拉断力
    F_break: float = params.F_break
    # 最大比例
    max_f_pull_rate: float = max_f_pull * 0.001 / F_break
    # 获取单位浮重
    unit_weight: float = get_instrument_unit_weight(params)
    # 测井工具在空气中的重量
    m1: float = params.m1
    # 测井工具长度
    L1: float = params.L1
    f_push_cable_tension_depth: float = result_cable_tension[0].depth
    f_push_cable_tension: float = round(result_cable_tension[0].F_push / 1000, 2)
    f_pull_cable_tension_depth: float = result_cable_tension[0].depth
    f_pull_cable_tension: float = round(result_cable_tension[0].F_pull / 1000, 2)
    f_push_pull: float = round((result_cable_strain[0].F_pull - result_cable_strain[0].F_push)/1000, 2)
    pull_push_rate: float = round(result_cable_strain[0].F_pull / result_cable_strain[0].F_push, 2)
    depth: float = result_cable_strain[0].depth
    f_pull_bottom: float = round(result_cable_strain[0].F_pull / 1000, 2)

    for cable_item in result_cable_tension:
        if cable_item.depth == hel_depth:
            f_push_cable_tension = round(cable_item.F_push / 1000, 2)
            f_pull_cable_tension = round(cable_item.F_pull / 1000, 2)
    if is_not_descent_bottom == False:
        f_push_cable_tension_depth = target_item.depth
        f_pull_cable_tension_depth = target_item.depth
        f_pull_cable_tension = f_pull_cable_tension
        f_push_cable_tension = f_push_cable_tension
        f_push_pull = round((target_item.F_pull - target_item.F_push) / 1000, 2)
        pull_push_rate = round(target_item.F_pull / target_item.F_push, 2)
        depth = target_item.depth
        f_pull_bottom: float = round(target_item.F_pull / 1000, 2)
    # 计算weak力基准值
    # base_val: float = round(round((0.5 * F_break - f_pull_bottom + (unit_weight * L1) / 1000), 2) * 1000 * 0.225, 2)
    base_val: int = round((params.F_weak_low + params.F_weak_high) / 8.9)
    instrument_unit_weight: float = round(m1 * L1 * 9.81 / 1000, 2)
    min_f_pushs: float = round(min_f_push / 1000, 2)
    max_f_pulls: float = round(max_f_pull / 1000, 2)
    f_break: float = params.F_break
    if params.is_Not_metric == False:
        instrument_unit_weight = round(m1 * L1 * 9.81 / 1000 * 224.81, 2)
        f_push_cable_tension = round(f_push_cable_tension * 224.81, 2)
        f_pull_cable_tension = round(f_pull_cable_tension * 224.81, 2)
        min_f_pushs: float = round(min_f_push / 1000 * 224.81, 2)
        max_f_pulls: float = round(max_f_pull / 1000 * 224.81, 2)
        f_break: float = round(params.F_break * 224.81, 2)
        f_push_pull = round((target_item.F_pull - target_item.F_push) / 1000 * 224.81, 2)

    # 取两位小数点
    return {
        # 仪器组合在空气中的重量为4.07kN
        "instrument_unit_weight": instrument_unit_weight,
        # 目前仪器组合重量是马笼头弱点值的17.27%。
        "instrument_mlt_rate": round((m1 * L1 * 9.81 / 1000) / (base_val * 4.45 / 1000), 4),
        # 仪器串最大外径是90mm
        "max_out_diameter": params.d1 * 1000,
        # 仪器串与井眼间距120mm，两边间隔为60mm。
        "instrument_between_well": round((params.d_oh - params.d_max1) * 1000, 2),
        "distance_between_two_sides": round((params.d_oh - params.d_max1) * 1000 / 2, 2),
        # 仪器串下放到2100.0m处，电缆头张力为-0.80kN   不能正常下放就是1843对应的值
        "f_push_cable_tension_depth": f_push_cable_tension_depth,
        "f_push_cable_tension": f_push_cable_tension,
        # 仪器串上提到2100.0m处，电缆头张力为0.80kN。
        "f_pull_cable_tension_depth": f_pull_cable_tension_depth,
        "f_pull_cable_tension": f_pull_cable_tension,
        # 最小预测电缆张力为电缆下放到16.0m处，此时地面张力值为2.86kN。
        "min_f_push_depth": min_f_push_depth,
        "min_f_push": min_f_pushs,
        # 最大预测电缆张力为电缆上提至1798.0m处，此时地面张力值为12.85kN
        "max_f_pull_depth": max_f_pull_depth,
        "max_f_pull": max_f_pulls,
        # 此张力达到电缆额定拉断力的89.0kN的14.43%。
        "f_break": f_break,
        "max_f_pull_rate": round(max_f_pull_rate, 4),
        # 在通常条件下，在2100.0m处由下放切换至上提，在地面预计会产生7.38kN的拉力变化。
        "depth": depth,
        "f_push_pull": f_push_pull,
        # 此张力变化比为3.08
        "pull_push_rate": pull_push_rate,

    }


# 拉断弱点分析
@router.post('/v3/weak/conclusion')
async def weak_tension_conclusion(params: ReqSoftParams):
    # 计算 MD5 哈希值
    md5_hash = params.calc_md5()
    # 参数预处理
    soft_params: SoftParams = SoftParams(params)
    # 获取井号
    wellId: str = params.wellId
    # 获取间距
    space: float = params.space
    # 获取是否有造斜点
    is_slanting_point = params.is_slanting_point
    # 构建对象名称
    data_object_name_cable_strain = wellId + '/' + md5_hash + '-strain.xlsx'
    # 构建可下入深度对象名称
    data_object_name_descent_depth = wellId + '/' + md5_hash + '-descent_depth.xlsx'
    result_cable_strain: List = []
    # 是否加入液体阻力 @TODO 如果为true就加入 否则加0
    is_Not_add_resistance = params.add_liquid_resistance
    try:
        client.stat_object('well', data_object_name_cable_strain)
        print('已存在对应变形量分析数据')
        file_data = client.get_object("well", data_object_name_cable_strain)
        df = pd.read_excel(BytesIO(file_data.read()))
        result_cable_strain = []
        for index, row in df.iterrows():
            result_cable_strain.append(row)
    except S3Error:
        print('不存在变形量分析数据')
        # 获取插值数据
        _list_: List[InterpolationData] = read_interpolation_data(wellId, space, is_slanting_point)
        max_depth: float = _list_[-1].depth
        inc_dict: Dict = {}
        for item in _list_:
            inc_dict[item.depth] = item.inclination
        # soft_params.build_f_hel(inc_dict, max_depth)
        # 计算的间距
        calculate_space = params.calculate_space
        last_depth = _list_[-1].depth
        # 设置间隔深度列表
        desired_depths = [last_depth]
        # 从最后一个深度开始，减去间距，直到小于间距为止 这样就只计算部分深度
        while desired_depths[-1] > calculate_space:
            new_value = desired_depths[-1] - calculate_space
            desired_depths.append(new_value)
            # 对列表进行排序并去除重复值
        desired_depths = sorted(set(desired_depths), reverse=True)

        # 是否加入液体阻力 @TODO 如果为true就加入 否则加0
        is_Not_add_resistance = params.add_liquid_resistance
        # 井口摩擦力设置
        wellhead_friction: float = params.wellhead_friction
        result_cable_strain = strain_run(soft_params, _list_, is_Not_add_resistance, desired_depths, wellhead_friction)
        save('well', data_object_name_cable_strain, result_cable_strain)
    try:
        client.stat_object('well', data_object_name_descent_depth)
        print('已存在对应可下入深度分析数据')
        file_data = client.get_object("well", data_object_name_descent_depth)
        df = pd.read_excel(BytesIO(file_data.read()))
        result_descent_depth = []
        for index, row in df.iterrows():
            result_descent_depth.append(row)
    except S3Error:
        print('不存在可下入深度分析分析数据')
        # 获取插值数据
        _list_: List[InterpolationData] = read_interpolation_data(wellId, space, is_slanting_point)
        inc_depth: float = _list_[-1].depth
        # 获取给定的井斜角的值
        inc_value = params.inclination
        inc_dict: Dict = {}
        for item in _list_:
            inc_dict[item.depth] = item.inclination
            if item.inclination > inc_value:
                inc_depth = item.depth
                break
        print("当前的井斜角对应的深度为为", inc_depth)
        # 计算的间距
        calculate_space = params.calculate_space
        last_depth = _list_[-1].depth
        # 设置间隔深度列表
        desired_depths = [last_depth]
        # 从最后一个深度开始，减去间距，直到小于间距为止 这样就只计算部分深度
        while desired_depths[-1] > calculate_space:
            new_value = desired_depths[-1] - calculate_space
            desired_depths.append(new_value)
            # 对列表进行排序并去除重复值
        desired_depths = sorted(set(desired_depths), reverse=True)

        # 是否加入液体阻力 @TODO 如果为true就加入 否则加0
        is_Not_add_resistance = params.add_liquid_resistance
        # print(inc_dict)
        result_descent_depth = descent_run(soft_params, inc_depth, _list_, is_Not_add_resistance, desired_depths)
        save('well', data_object_name_descent_depth, result_descent_depth)

    # ********************************************
    # # 拿到拦截的极限下入深度
    hel_depth: float = result_descent_depth[0].depth
    # 是否可以下入到极限深度：井底 能下入就是 True 不能正常就是False
    is_not_descent_bottom: bool = True

    for descent_item in result_descent_depth:
        if descent_item.push_cable_pressure == 1:
            is_not_descent_bottom = False
            hel_depth = descent_item.depth
    # 发生屈曲的迭代开始深度 start_depth 1890 屈曲点 hel_depth 1843
    start_depth: float = 0.0
    # 获取 1883 全部值
    target_item = result_cable_strain[0].F_weak_rate_low
    for f_item in result_cable_strain:
        if f_item.depth == hel_depth:
            target_item = f_item
            break
    # # 标记是否找到确切匹配的标志
    # exact_match_found = False
    #
    # for f_item in result_cable_strain:
    #     if f_item.depth == hel_depth:
    #         target_item = f_item
    #         exact_match_found = True
    #         break
    #
    # if not exact_match_found:
    #     # 如果没有找到确切匹配的值，寻找比hel_depth小的最近值
    #     closest_item = None
    #     for f_item in result_cable_strain:
    #         if f_item.depth < hel_depth:
    #             if closest_item is None or f_item.depth > closest_item.depth:
    #                 closest_item = f_item
    #     if closest_item is not None:
    #         target_item = closest_item
    # 最大张力及产生位置 F_weak_low
    max_f_weak_low: float = 0.0
    max_f_weak_low_depth: float = 0.0
    # % 最大张力及产生位置 F_weak_rate_low
    max_f_weak_rate_low: float = 0.0
    max_f_weak_rate_low_depth: float = 0.0
    f_weak_depth_pull: float = result_cable_strain[0].F_pull

    # 取正常上提和下放的最大张力 默认最大张力为
    max_f_pull: float = 0.0
    max_f_push: float = 0.0
    for item in result_cable_strain:
        # 计算最大上提F
        if item.depth <= hel_depth:

            if item.F_weak_low > max_f_weak_low:
                max_f_weak_low = item.F_weak_low
                max_f_weak_low_depth = item.depth
                f_weak_depth_pull = item.F_pull
            if item.F_weak_rate_low > max_f_weak_rate_low:
                max_f_weak_rate_low = item.F_weak_rate_low
                max_f_weak_rate_low_depth = item.depth
            if item.F_pull > max_f_pull:
                max_f_pull = item.F_pull
            if item.F_push > max_f_push:
                max_f_push = item.F_push

    max_value = max(max_f_pull, max_f_push) / 1000
    # 在井底 地面最大电缆张力
    bottom_depth: float = result_cable_strain[0].depth
    bottom_max_weak_high: float = round(result_cable_strain[0].F_weak_high / 1000, 2)
    # 获取单位浮重
    unit_weight: float = get_instrument_unit_weight(params)
    # 测井工具长度
    L1: float = params.L1
    # 获取拉断力
    F_break: float = params.F_break

    f_pull_bottom: float = round(result_cable_strain[0].F_pull / 1000, 2)
    # 计算weak力基准值
    # base_val: int = round((params.F_weak_low + params.F_weak_high) / 8.9)
    if is_not_descent_bottom == False:
        f_pull_bottom: float = round(target_item.F_pull / 1000, 2)
        bottom_depth: float = target_item.depth
        bottom_max_weak_high: float = round(target_item.F_weak_high / 1000, 2)
    # 最大比例
    max_f_weak_rate: float = bottom_max_weak_high / F_break
    base_val: float = round(round((0.5 * F_break - f_pull_bottom + (unit_weight * L1) / 1000), 2) * 1000 * 0.225, 2)
    f_weak_break: float = params.F_break
    f_low_pull_change: float = round(bottom_max_weak_high - f_weak_depth_pull / 1000, 2)
    f_low_pull_rate: float = round(bottom_max_weak_high / (f_weak_depth_pull / 1000), 2)
    if params.is_Not_metric == False:
        bottom_max_weak_high: float = round(bottom_max_weak_high * 224.81, 2)
        f_weak_break: float = round(f_weak_break * 224.81, 2)
        f_low_pull_change: float = round(bottom_max_weak_high - f_weak_depth_pull / 1000 * 224.81, 2)

    # 取两位小数点
    return {
        # 在2100.0m处拉断弱点时，地面最大电缆张力为64.27kN。
        "max_f_weak_rate_low_depth": bottom_depth,
        "max_f_weak_rate_low": bottom_max_weak_high,
        # "max_f_weak_rate_low": round(max_f_weak_low / 1000, 2),
        # 最大张力是电缆额定拉断力89.0kN的72.21%
        "f_weak_break": f_weak_break,
        "max_f_weak_rate": round(max_f_weak_rate, 4),
        # 电缆允许操作强度为73.0%
        "max_f_weak_range": math.ceil(max_f_weak_rate * 100) / 100,
        # 弱点拉脱瞬间，在地面张力变化为53.35kN
        # "f_low_pull_change": round(bottom_max_weak_high - f_weak_depth_pull / 1000, 2),
        "f_low_pull_change": f_low_pull_change,
        # 此张力变化比为5.89
        # "f_low_pull_rate": round(bottom_max_weak_high / (f_weak_depth_pull / 1000), 2),
        "f_low_pull_rate": f_low_pull_rate,
        # 推荐马笼头弱点设置范围为7760lbf - 8240.0lbf
        "f_break_tensile_rod": round(round((0.5 * F_break - f_pull_bottom + (unit_weight * L1) / 1000), 2) * 1000 * 0.225, 2),
        "f_break_tensile_rod_low": round(base_val * (1 - 0.03), 2),
        "f_break_tensile_rod_high": round(base_val * (1 + 0.03), 2),
        "f_break_tensile_rod_range": "({}±{})".format(round(base_val, 2), "3%"),

    }


